/*//////////////////////////////////////////////////////////////////////////////
//
//                  INTEL CORPORATION PROPRIETARY INFORMATION
//     This software is supplied under the terms of a license agreement or
//     nondisclosure agreement with Intel Corporation and may not be copied
//     or disclosed except in accordance with the terms of that agreement.
//          Copyright(c) 2002-2008 Intel Corporation. All Rights Reserved.
//
*/

#include "umc_defs.h"
#if defined (UMC_ENABLE_MP3_AUDIO_ENCODER)

#include "umc_mp3_encoder.h"
#include "umc_mp3_encoder_params.h"
#include "mp3enc.h"
#include "vm_debug.h"

namespace UMC {

  AudioCodec *CreateMP3Encoder() { return (new MP3Encoder); }

  MP3Encoder::MP3Encoder()
  {
    state = NULL;
    m_freq = 0;
    m_bitrate = 0;
    m_stereo = 0;
    m_id = 0;
    m_frame_size = 0;
    m_layer = 0;
    m_stereo_mode = 0;
    m_ns_mode = 0;
    m_force_mpeg1 = 0;
    m_mc_matrix_procedure = 0;
    stateMemId = 0;
    m_initialized = 0;
  }

  MP3Encoder::~MP3Encoder()
  {
    Close();
  }

  IppBool MP3Encoder::CheckBitRate(Ipp32s sample_rate,
                                   Ipp32s layer,
                                   Ipp32s bitrate,
                                   Ipp32s stereo)
  {
      Ipp32s ind;
      Ipp32s id;

      if (sample_rate == 44100 || sample_rate == 48000 || sample_rate == 32000)
        id = 1;
      else if (sample_rate == 22050 || sample_rate == 24000 || sample_rate == 16000)
        id = 0;
      else return ippFalse;

      if (mp3enc_checkBitRate(id, layer, stereo, bitrate / 1000, &ind))
          return ippTrue;

      return ippFalse;
  }

#undef  VM_DEBUG_FUNC_NAME
#define VM_DEBUG_FUNC_NAME VM_STRING("MP3Encoder::Init")

  Status MP3Encoder::Init(BaseCodecParams * init)
  {
    Ipp32s size;
    Ipp32s st, br, fr, chm, channel_mask;
    Ipp32s stereo_mode;
    MP3Status res;
    MP3EncoderParams* pMP3EncoderParams;

    // checks or create memory allocator;
    if (BaseCodec::Init(init) != UMC_OK) {
      vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("Failed to create allocator!\n"));
      return UMC_ERR_ALLOC;
    }

    AudioCodecParams* pAudioCodecInit =
      DynamicCast<AudioCodecParams, BaseCodecParams>(init);

    if (!pAudioCodecInit)
      return UMC_ERR_NULL_PTR;

    fr = pAudioCodecInit->m_info_in.sample_frequency;
    st = pAudioCodecInit->m_info_in.channels;
    chm = pAudioCodecInit->m_info_in.channel_mask;
    br = pAudioCodecInit->m_info_out.bitrate / 1000;

    vm_debug_trace1(VM_DEBUG_INFO, VM_STRING("sample_frequency = %d"), fr);
    vm_debug_trace1(VM_DEBUG_INFO, VM_STRING("channels = %d"), st);
    vm_debug_trace1(VM_DEBUG_INFO, VM_STRING("bitrate = %d"), br);

    m_frame_num = 0;
    m_pts_prev = 0;

    pMP3EncoderParams = DynamicCast<MP3EncoderParams, BaseCodecParams>(init);
    if (pMP3EncoderParams) {
      m_stereo_mode = pMP3EncoderParams->stereo_mode;
      m_ns_mode = pMP3EncoderParams->ns_mode;
      m_layer = pMP3EncoderParams->layer;
      m_force_mpeg1 = pMP3EncoderParams->force_mpeg1;
      m_mc_matrix_procedure = pMP3EncoderParams->mc_matrix_procedure;
      m_mc_lfe_filter_off = pMP3EncoderParams->mc_lfe_filter_off;
      m_br_mode = pMP3EncoderParams->mode;
    } else {
      m_stereo_mode = UMC_MPA_LR_STEREO;
      m_ns_mode = 0;
      m_layer = 3;
      m_force_mpeg1 = 0;
      m_mc_matrix_procedure = 0;
      m_mc_lfe_filter_off = 0;
      m_br_mode = MPAENC_CBR;
    }

    vm_debug_trace1(VM_DEBUG_INFO, VM_STRING("m_layer = %d"), m_layer);
    vm_debug_trace1(VM_DEBUG_INFO, VM_STRING("m_force_mpeg1 = %d"), m_force_mpeg1);
    vm_debug_trace1(VM_DEBUG_INFO, VM_STRING("m_ns_mode = %d"), m_ns_mode);

    if (st == 1)
      m_stereo_mode = UMC_MPA_MONO;

    switch (m_stereo_mode) {
      case UMC_MPA_MONO:
        stereo_mode = MPA_MONO; break;
      case UMC_MPA_LR_STEREO:
        stereo_mode = MPA_LR_STEREO; break;
      case UMC_MPA_MS_STEREO:
        stereo_mode = MPA_MS_STEREO; break;
      case UMC_MPA_JOINT_STEREO:
        stereo_mode = MPA_JOINT_STEREO; break;
      default:
        stereo_mode = MPA_LR_STEREO; break;
    };

    vm_debug_trace1(VM_DEBUG_INFO, VM_STRING("stereo_mode = %d"), stereo_mode);

    channel_mask = 0;

    if (chm & (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT))
      channel_mask |= MP3_CHANNEL_STEREO;

    if (chm & CHANNEL_FRONT_CENTER)
      channel_mask  |= MP3_CHANNEL_CENTER;

    if (chm & CHANNEL_LOW_FREQUENCY)
      channel_mask  |= MP3_CHANNEL_LOW_FREQUENCY;

    if (chm & CHANNEL_BACK_CENTER)
      channel_mask  |= MP3_CHANNEL_SURROUND_MONO;

    if (chm & (CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT))
      channel_mask |= MP3_CHANNEL_SURROUND_STEREO;

    /*    if (chm & (CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT))
    channel_mask |= MP3_CHANNEL_SURROUND_STEREO_P2;*/

    res = mp3encInit(NULL, fr, st, channel_mask, m_layer, br, m_br_mode, stereo_mode, m_ns_mode,
      m_force_mpeg1, m_mc_matrix_procedure, m_mc_lfe_filter_off, &size);
    if (res != MP3_OK)
      return StatusMP3_2_UMC(res);

    if (m_pMemoryAllocator->Alloc(&stateMemId, size,
      UMC_ALLOC_PERSISTENT) != UMC_OK) {
        vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("External allocation failed\n"));
        return UMC_ERR_ALLOC;
      }

    state = (MP3Enc *)m_pMemoryAllocator->Lock(stateMemId);
    if(!state) {
      vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("External Lock failed\n"));
      return UMC_ERR_ALLOC;
    }

    res = mp3encInit(state, fr, st, channel_mask, m_layer, br, m_br_mode, stereo_mode, m_ns_mode,
      m_force_mpeg1, m_mc_matrix_procedure, m_mc_lfe_filter_off, &size);

    if (res == MP3_OK) {
        m_stereo = st;
        m_freq = fr;
        m_bitrate = br;
        mp3encGetMpegId(state, &m_id);
        vm_debug_trace1(VM_DEBUG_INFO, VM_STRING("MPEG1/2 = %d"), 2 - m_id);
        mp3encGetUpsample(state, &m_upsample);
        mp3encGetFrameSize(&m_frame_size, m_id, m_layer, m_upsample);
        m_initialized = 1;
    }

    MemUnlock();

    return StatusMP3_2_UMC(res);
  }
#undef  VM_DEBUG_FUNC_NAME
#define VM_DEBUG_FUNC_NAME NULL

  Status MP3Encoder::Close()
  {
    Status res = MemLock();
    if (res != UMC_OK) {
      vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("MemLock failed\n"));
      return UMC_OK;
    }
    mp3encClose(state);
    MemUnlock();

    if(state) {
      m_pMemoryAllocator->Free(stateMemId);
      state = NULL;
    }

    BaseCodec::Close();

    return UMC_OK;
  }

  Status  MP3Encoder::GetFrame(MediaData * in, MediaData * out) {
    Ipp32s inSamples;
    Ipp32s bytes;
    MP3Status res;
    Ipp32s out_size, slot_size;
    Status status;
    Ipp64f pts_start, pts_end;
    Ipp32s nullInput = 0;

    if (!out || !out->GetDataPointer())
      return UMC_ERR_NULL_PTR;

    if (!m_initialized)
      return UMC_ERR_NOT_INITIALIZED;

    if (!in || !in->GetDataPointer()) {
      nullInput = 1;
    }

    if (!nullInput) {
      inSamples = (in->GetDataSize() / sizeof(Ipp16s));

      if (inSamples < m_stereo * m_frame_size)
        return UMC_ERR_NOT_ENOUGH_DATA;
    }

    inSamples = m_stereo * m_frame_size;

    out_size = (out->GetBufferSize() - ((Ipp8u*)out->GetDataPointer() -
      (Ipp8u*)out->GetBufferPointer()));

    status = MemLock();
    if (status != UMC_OK) {
      vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("MemLock failed\n"));
      return status;
    }

    mp3encGetSlotSize(state, &slot_size);
    if (out_size < slot_size + (m_layer == 3 ? 1024 : 0)) {
      MemUnlock();
      return UMC_ERR_NOT_ENOUGH_BUFFER;
    }

    if (!nullInput) {
      res = mp3encGetFrame((Ipp16s *)in->GetDataPointer(), &bytes,
          (Ipp8u *)out->GetDataPointer(), state);
    } else {
      res = mp3encGetFrame(NULL, &bytes,
                          (Ipp8u *)out->GetDataPointer(), state);
    }

    if (res == MP3_OK) {
      if (!nullInput) {
        pts_start = in->GetTime();
      } else {
        pts_start = -1;
      }

      if (pts_start < 0)
        pts_start = m_pts_prev;

      m_pts_prev = pts_end = pts_start+(Ipp32f)(inSamples/m_stereo)/(Ipp32f)(m_freq);

      out->SetTime(pts_start, pts_end);
      out->SetDataSize(bytes);

      if (!nullInput) {
        in->MoveDataPointer(inSamples*sizeof(Ipp16s));
        in->SetTime(pts_end);

        m_frame_num++;
      }
    }

    MemUnlock();
    return StatusMP3_2_UMC(res);
  }

  Status MP3Encoder::GetInfo(BaseCodecParams * info)
  {
    if (!info)
      return UMC_ERR_NULL_PTR;

    info->m_SuggestedInputSize = m_stereo * sizeof(Ipp16s) * m_frame_size;
    //info->m_SuggestedOutputSize = 4096;
    info->m_SuggestedOutputSize = m_stereo * sizeof(Ipp16s) * m_frame_size;
    if (m_layer == 3)
      info->m_SuggestedOutputSize += 1024;

    AudioCodecParams *pAudioCodecInfo =
      DynamicCast < AudioCodecParams, BaseCodecParams > (info);

    if (!pAudioCodecInfo)
      return UMC_OK;

    pAudioCodecInfo->m_info_in.bitPerSample = 16;
    pAudioCodecInfo->m_info_out.bitPerSample = 16;
    pAudioCodecInfo->m_info_in.bitrate = 0;
    pAudioCodecInfo->m_info_out.bitrate = 1000*m_bitrate;
    pAudioCodecInfo->m_info_in.channels = m_stereo;
    pAudioCodecInfo->m_info_out.channels = m_stereo;

    pAudioCodecInfo->m_info_in.sample_frequency = m_freq;
    pAudioCodecInfo->m_info_out.sample_frequency = m_freq << m_upsample;

    pAudioCodecInfo->m_info_in.stream_type = PCM_AUDIO;
    pAudioCodecInfo->m_info_out.stream_type = UNDEF_AUDIO;

    if (m_layer == 3) {
      if (m_id == 1)
        pAudioCodecInfo->m_info_out.stream_type = MP1L3_AUDIO;
      else if (m_id == 0)
        pAudioCodecInfo->m_info_out.stream_type = MP2L3_AUDIO;
    }
    else if (m_layer == 2) {
      if (m_id == 1)
        pAudioCodecInfo->m_info_out.stream_type = MP1L2_AUDIO;
      else if (m_id == 0)
        pAudioCodecInfo->m_info_out.stream_type = MP2L2_AUDIO;
    }
    else if (m_layer == 1) {
      if (m_id == 1)
        pAudioCodecInfo->m_info_out.stream_type = MP1L1_AUDIO;
      else if (m_id == 0)
        pAudioCodecInfo->m_info_out.stream_type = MP2L1_AUDIO;
    }

    return UMC_OK;
  }

  Status MP3Encoder::GetDuration(Ipp32f *p_duration)
  {
    Ipp32f duration;

    if (!p_duration)
      return UMC_ERR_NULL_PTR;

    if (!m_initialized)
      return UMC_ERR_NOT_INITIALIZED;

    duration = (Ipp32f)m_frame_num * m_frame_size;

    duration /= (Ipp32f)(m_freq);

    p_duration[0] = duration;

    return UMC_OK;
  }

  Status MP3Encoder::SetParams(BaseCodecParams * /* params */) {
    return UMC_ERR_NOT_IMPLEMENTED;
  }

  Status MP3Encoder::Reset() {
    return UMC_ERR_NOT_IMPLEMENTED;
  }

  Status MP3Encoder::StatusMP3_2_UMC(MP3Status st)
  {
      Status res;

      if (st == MP3_OK)
          res = UMC_OK;
      else if (st == MP3_NOT_ENOUGH_DATA)
          res = UMC_ERR_NOT_ENOUGH_DATA;
      else if (st == MP3_BAD_FORMAT)
          res = UMC_ERR_INVALID_STREAM;
      else if (st == MP3_ALLOC)
          res = UMC_ERR_ALLOC;
      else if (st == MP3_BAD_STREAM)
          res = UMC_ERR_INVALID_STREAM;
      else if (st == MP3_NULL_PTR)
          res = UMC_ERR_NULL_PTR;
      else if (st == MP3_NOT_FIND_SYNCWORD)
          res = UMC_ERR_SYNC;
      else if (st == MP3_NOT_ENOUGH_BUFFER)
          res = UMC_ERR_NOT_ENOUGH_BUFFER;
      else if (st == MP3_UNSUPPORTED)
          res = UMC_ERR_UNSUPPORTED;
      else if (st == MP3_FAILED_TO_INITIALIZE)
          res = UMC_ERR_INIT;
      else if (st == MP3_END_OF_STREAM)
        res = UMC_ERR_END_OF_STREAM;
      else
          res = UMC_ERR_UNSUPPORTED;

      return res;
  }

  Status MP3Encoder::MemLock() {
    MP3Enc *pOldState = state;

    if (!m_pMemoryAllocator) {
      vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("External Lock failed: m_pMemoryAllocator is NULL\n"));
      return UMC_ERR_ALLOC;
    }

    state = (MP3Enc *)m_pMemoryAllocator->Lock(stateMemId);
    if(!state) {
      vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("External Lock failed\n"));
      return UMC_ERR_ALLOC;
    }
    if (state != pOldState) {
      mp3encUpdateMemMap(state, (Ipp8u *)state-(Ipp8u *)pOldState);
    }
    return UMC_OK;
  }

  Status MP3Encoder::MemUnlock() {
    if (stateMemId) {
      if (m_pMemoryAllocator->Unlock(stateMemId) != UMC_OK) {
        vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("External Unlock failed\n"));
        return UMC_ERR_ALLOC;
      }
    }
    return UMC_OK;
  }
};      // namespace UMC

#endif //UMC_ENABLE_MP3_AUDIO_ENCODER
